/**
 * Copyright 2009-2016 the original author or authors.
 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.ibatis.builder;

import org.apache.ibatis.binding.Page;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlSource;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Field;
import java.util.Map;
import java.util.Properties;

@Intercepts({@Signature(
        type = Executor.class,
        method = "query",
        args = {MappedStatement.class,
                Object.class,
                RowBounds.class,
                ResultHandler.class})})
public class PagePlugin implements Interceptor {
    private final static Logger logger = LoggerFactory.getLogger(PagePlugin.class);

    private Properties properties;

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        logger.info("====>");
        logger.info("1====> {} ", invocation.getTarget());
        logger.info("2====> {} ", invocation.getMethod());
        logger.info("3====> {} ", invocation.getArgs());
        Object[] args = (Object[]) invocation.getArgs();
        MappedStatement mappedStatement = (MappedStatement) args[0];
        SqlSource sqlSource = null;
        Object parameters = args[1];
        String sql = mappedStatement.getBoundSql(parameters).getSql();
        if (parameters instanceof Map) {
            Map params = (Map) parameters;
            Object pageObj = params.get("page");
            Page page = pageObj == null ? null : (Page) pageObj;
            if (page != null) {
                sql = sql + " limit " + page.getPageCount() + "," + page.getPageSize();
                sqlSource = mappedStatement.getSqlSource();
                printField(sqlSource);
                BoundSql boundSql = mappedStatement.getBoundSql(parameters);
                setFieldValue(boundSql, "sql", sql);
                setFieldValue(mappedStatement, "sqlSource", sqlSource);
            }
            System.out.println(sql);
        }
        RowBounds rowBounds = (RowBounds) args[2];
        logger.info("==> rowBounds {}", rowBounds);
        logger.info("====> {} ", mappedStatement);
        return invocation.proceed();
    }

    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }

    @Override
    public void setProperties(Properties properties) {
        this.properties = properties;
    }

    public Properties getProperties() {
        return properties;
    }


    private void setFieldValue(Object object, String name, Object value) {
        Field[] fs = object.getClass().getDeclaredFields();
        try {
            for (Field field : fs) {
                if(field.getName().equals(name)) {
                    field.setAccessible(true);
                    field.set(object, value);
                    break;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    private void printField(Object object) {
        Field[] fs = object.getClass().getDeclaredFields();
        try {
            for (Field field : fs) {
                if(field.getName().equals("sqlSource")) {
                    System.out.println(field.getName());
                    printField(field);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

    }
}
